/*	Copyright
 
 This file falls within the license domain specified in "IACoreLinearAlgebra.h"
 */


#ifndef _LAVector2D
#define _LAVector2D




/*	Why this class exists
 
 Although LAVector is very flexible and can easily accomodate 2D
 vectors, 2D Vectors are used often enough the the added overhead
 of LAVector would be too much for many cases. For this, LAVector2D
 exists.
 
 Unlike LAVector, LAVector2D is a concrete type and does not require
 an external math engine not does it require manual memory management
 since it does not derive from IAObject.
 
 The variables X and Y are used, though they obviously don't necessarily
 have to be X and Y as they relate to cartesian coordinates.
 */


#include "IAMath.hpp"

template <typename Type>
class	LAVector2D ;





template <typename Type>
class	LAVector2D
{
public:
	
	typedef	LAVector2D<Type>			Vector ;
	typedef	LAVector2D<Type>&			VectorR ;
	typedef	LAVector2D<Type> const&		VectorCR ;
	typedef	Type const					TypeC ;		// Type-Constant
	typedef	Type&						TypeR ;		// Type-Reference
	typedef	Type const&					TypeCR ;	// Type-Constant-Reference
	
	
	
	
	
	
#pragma mark	-	Construction
	
	LAVector2D () :
		dX	( 0 ) ,
		dY	( 0 )
		{
		
		}
	LAVector2D ( const LAVector2D & copy ) :
		dX	( copy.dX ) ,
		dY	( copy.dY )
		{
		
		}
	LAVector2D ( TypeCR x , TypeCR y ) :
		dX	( x ) ,
		dY	( y )
		{
		
		}
	
	
	
	
	
	
#pragma mark	-	Getting Values, Mutation & Comparison
	
	Type	dX ;
	Type	dY ;
	
	bool	operator==	( VectorCR equalsThis ) const {
		return equalsThis.dX==dX && equalsThis.dY==dY ;
	}
	bool	operator!=	( VectorCR doesNotEqualThis ) const {
		return doesNotEqualThis.dX!=dX || doesNotEqualThis.dY!=dY ;
	}
	void	operator=	( VectorCR setToThis ) {
		dX = setToThis.dX ;
		dY = setToThis.dY ;
	}
	
	// radians; normal orientation
	Type	angle () const {
		return IAMath :: Arg<Type> ( dX , dY ) ;
	}
	
	
	
	
	
	
#pragma mark	-	Math
	
	
	// ThisVector/Vector to Scalar
	TypeC	operator *	( VectorCR dotThis ) const {
		return	dX*dotThis.dX + dY*dotThis.dY ;
	}
	
	
	// ThisVector/Vector to ThisVector
	void	operator +=	( VectorCR plusThis ) {
		dX	+=	plusThis.dX ;
		dY	+=	plusThis.dY ;
	}
	void	operator -=	( VectorCR minusThis ) {
		dX	-=	minusThis.dX ;
		dY	-=	minusThis.dY ;
	}
	
	
	// ThisVector/Scalar to ThisVector
	void	operator *=	( TypeCR timesThis ) {
		dX *= timesThis ;
		dY *= timesThis ;
	}
	void	operator /=	( TypeCR dividedByThis ) {
		dX /= dividedByThis ;
		dY /= dividedByThis ;
	}
	
	
	// ThisVector to Scalar
	// Note: norm2 & normN are only defined for Types where
	// ::sqrt and ::power are valid
	Type	norm1	() const {
		return IAMath::abs<Type>(dX) + IAMath::abs<Type>(dY) ;
	}
	Type	norm2	() const {
		return IAMath :: sqrt<Type> ( dX*dX + dY*dY ) ;
	}
	Type	normN	( unsigned n ) const {
		Type x , y ;
		if ( n & 1 ) // odd
			{
			x = IAMath::abs<Type>( dX ) ;
			y = IAMath::abs<Type>( dY ) ;
			}
		else
			{
			x = dX ;
			y = dY ;
			}
		Type Xa=x , Ya=y ; // accumulators
		while ( --n )
			{
			Xa *= x ;
			Ya *= y ;
			}
		return IAMath :: power<Type> ( Xa+Ya , 1./((Type)(n)) ) ;
	}
	
	
	// ThisVector to Scalar
	Type	max		() const {
		if ( dX > dY )
			return dX ;
		return dY ;
	}
	
	
	// ThisVector to ThisVector  &  ThisVector/Scalar to ThisVector
	void	normalize () {
		Type	norm = norm2 () ;
		dX /= norm ;
		dY /= norm ;
	} // norm2
	void	normalize ( unsigned byNormN ) {
		Type	norm ;
		if ( byNormN == 2 )
			norm = norm2 () ;
		else if ( byNormN == 1 )
			norm = norm1 () ;
		else
			norm = normN ( byNormN ) ;
		dX /= norm ;
		dY /= norm ;
	}
	
	
	// ThisVector to ThisVector
	// Uses standard rotation angles where 0 is x=1,y=0
	void	rotate90Degrees () {
		Type temp = -dY ;
		dY = dX ;
		dX = temp ;
	}
	void	rotate180Degrees () {
		dX = -dX ;
		dY = -dY ;
	}
	
} ;




#endif
